package com.nx.platform.es.common.utils;

import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Stream;

/**
 * @author
 * @since 2016年11月11日
 */
public class MoreMaps {

    /**
     * @param map
     * @param key
     * @return
     */
    public static boolean containsKey(final Map<?, ?> map, Object key) {
        return map != null && key != null && map.containsKey(key);
    }

    /**
     * @param map
     * @return
     */
    public static boolean isEmpty(final Map<?, ?> map) {
        return map == null || map.isEmpty();
    }

    /**
     * @param map
     * @return
     */
    public static boolean isNotEmpty(final Map<?, ?> map) {
        return !isEmpty(map);
    }

    /**
     * @param map
     * @param key
     * @return
     */
    public static String getString(final Map<?, ?> map, final Object key) {
        if (map != null) {
            final Object answer = map.get(key);
            if (answer != null) {
                return String.valueOf(answer);
            }
        }
        return null;
    }

    /**
     * @param map
     * @param key
     * @param defaultValue
     * @return
     */
    public static String getString(final Map<?, ?> map, final Object key, final String defaultValue) {
        final String answer = getString(map, key);
        if (answer == null) {
            return defaultValue;
        }
        return answer;
    }


    /**
     * @param map
     * @param key
     * @return
     */
    public static Integer getInteger(final Map<?, ?> map, final Object key) {
        final Number answer = getNumber(map, key);
        if (answer == null) {
            return null;
        }
        if (answer instanceof Integer) {
            return (Integer) answer;
        }
        return answer.intValue();
    }

    /**
     * @param map
     * @param key
     * @param defaultValue
     * @return
     */
    public static Integer getInteger(final Map<?, ?> map, final Object key, final Integer defaultValue) {
        Integer answer = getInteger(map, key);
        if (answer == null) {
            answer = defaultValue;
        }
        return answer;
    }


    /**
     * @param map
     * @param key
     * @return
     */
    public static int getIntValue(final Map<?, ?> map, final Object key) {
        final Integer answer = getInteger(map, key);
        if (answer == null) {
            return 0;
        }
        return answer;
    }

    /**
     * @param map
     * @param key
     * @param defaultValue
     * @return
     */
    public static int getIntValue(final Map<?, ?> map, final Object key, final int defaultValue) {
        final Integer answer = getInteger(map, key);
        if (answer == null) {
            return defaultValue;
        }
        return answer;
    }

    /**
     * @param map
     * @param key
     * @return
     */
    public static Long getLong(final Map<?, ?> map, final Object key) {
        final Number answer = getNumber(map, key);
        if (answer == null) {
            return null;
        }
        if (answer instanceof Long) {
            return (Long) answer;
        }
        return answer.longValue();
    }

    /**
     * @param map
     * @param key
     * @param defaultValue
     * @return
     */
    public static Long getLong(final Map<?, ?> map, final Object key, final Long defaultValue) {
        Long answer = getLong(map, key);
        if (answer == null) {
            answer = defaultValue;
        }
        return answer;
    }


    /**
     * @param map
     * @param key
     * @return
     */
    public static long getLongValue(final Map<?, ?> map, final Object key) {
        final Long answer = getLong(map, key);
        if (answer == null) {
            return 0;
        }
        return answer;
    }

    /**
     * @param map
     * @param key
     * @param defaultValue
     * @return
     */
    public static long getLongValue(final Map<?, ?> map, final Object key, final long defaultValue) {
        final Long answer = getLong(map, key);
        if (answer == null) {
            return defaultValue;
        }
        return answer;
    }

    /**
     * @param map
     * @param key
     * @return
     */
    public static Float getFloat(final Map<?, ?> map, final Object key) {
        final Number answer = getNumber(map, key);
        if (answer == null) {
            return null;
        }
        if (answer instanceof Float) {
            return (Float) answer;
        }
        return answer.floatValue();
    }

    /**
     * @param map
     * @param key
     * @param defaultValue
     * @return
     */
    public static Float getFloat(final Map<?, ?> map, final Object key, final Float defaultValue) {
        Float answer = getFloat(map, key);
        if (answer == null) {
            answer = defaultValue;
        }
        return answer;
    }

    /**
     * @param map
     * @param key
     * @return
     */
    public static float getFloatValue(final Map<?, ?> map, final Object key) {
        final Float answer = getFloat(map, key);
        if (answer == null) {
            return 0;
        }
        return answer;
    }

    /**
     * @param map
     * @param key
     * @param defaultValue
     * @return
     */
    public static float getFloatValue(final Map<?, ?> map, final Object key, final float defaultValue) {
        final Float answer = getFloat(map, key);
        if (answer == null) {
            return defaultValue;
        }
        return answer;
    }

    /**
     * @param map
     * @param key
     * @return
     */
    public static Double getDouble(final Map<?, ?> map, final Object key) {
        final Number answer = getNumber(map, key);
        if (answer == null) {
            return null;
        }
        if (answer instanceof Double) {
            return (Double) answer;
        }
        return answer.doubleValue();
    }

    /**
     * @param map
     * @param key
     * @param defaultValue
     * @return
     */
    public static Double getDouble(final Map<?, ?> map, final Object key, final Double defaultValue) {
        Double answer = getDouble(map, key);
        if (answer == null) {
            answer = defaultValue;
        }
        return answer;
    }

    /**
     * @param map
     * @param key
     * @return
     */
    public static double getDoubleValue(final Map<?, ?> map, final Object key) {
        final Double answer = getDouble(map, key);
        if (answer == null) {
            return 0;
        }
        return answer;
    }

    /**
     * @param map
     * @param key
     * @param defaultValue
     * @return
     */
    public static double getDoubleValue(final Map<?, ?> map, final Object key, final double defaultValue) {
        final Double answer = getDouble(map, key);
        if (answer == null) {
            return defaultValue;
        }
        return answer;
    }

    /**
     * @param map
     * @param key
     * @return
     */
    public static Number getNumber(final Map<?, ?> map, final Object key) {
        if (map != null) {
            final Object answer = map.get(key);
            if (answer != null) {
                if (answer instanceof Number) {
                    return (Number) answer;
                }
                if (answer instanceof String) {
                    try {
                        final String text = (String) answer;
                        return NumberFormat.getInstance().parse(text);
                    } catch (final ParseException e) { // NOPMD
                        // failure means null is returned
                    }
                }
            }
        }
        return null;
    }

    /**
     * @param map
     * @param key
     * @param defaultValue
     * @return
     */
    public static Number getNumber(final Map<?, ?> map, final Object key, final Number defaultValue) {
        final Number answer = getNumber(map, key);
        if (answer == null) {
            return defaultValue;
        }
        return answer;
    }

    public static Boolean getBoolean(final Map<?, ?> map, final Object key) {
        if (map != null) {
            final Object answer = map.get(key);
            if (answer != null) {
                if (answer instanceof Boolean) {
                    return (Boolean) answer;
                }
                if (answer instanceof String) {
                    return Boolean.valueOf((String) answer);
                }
                if (answer instanceof Number) {
                    final Number n = (Number) answer;
                    return n.intValue() != 0 ? Boolean.TRUE : Boolean.FALSE;
                }
            }
        }
        return null;
    }

    /**
     * @param map
     * @param key
     * @param defaultValue
     * @return
     */
    public static <K> Boolean getBoolean(final Map<?, ?> map, final Object key, final Boolean defaultValue) {
        Boolean answer = getBoolean(map, key);
        if (answer == null) {
            answer = defaultValue;
        }
        return answer;
    }

    /**
     * @param map
     * @param key
     * @return
     */
    public static boolean getBooleanValue(final Map<?, ?> map, final Object key) {
        final Boolean booleanObject = getBoolean(map, key);
        if (booleanObject == null) {
            return false;
        }
        return booleanObject;
    }

    /**
     * @param map
     * @param key
     * @param defaultValue
     * @return
     */
    public static boolean getBooleanValue(final Map<?, ?> map, final Object key, final boolean defaultValue) {
        final Boolean booleanObject = getBoolean(map, key);
        if (booleanObject == null) {
            return defaultValue;
        }
        return booleanObject;
    }

    /**
     * @param map
     * @param key
     * @param clazz
     * @return
     * @throws ClassCastException
     */
    @SuppressWarnings("unchecked")
    public static <T> T getObject(final Map<?, ?> map, final Object key)
            throws ClassCastException {
        if (map != null && key != null) {
            Object value = map.get(key);
            return (T) value;
        }
        return null;
    }

    public static <T> T getObject(final Map<?, ?> map, final Object key, final Class<T> clazz)
            throws ClassCastException {
        if (map != null && key != null && clazz != null) {
            Object value = map.get(key);
            if (value != null) {
                return clazz.cast(value);
            }
        }
        return null;
    }

    // String

    public static String[] getStringArray(Map<?, ?> map, Object key, Pattern splitter) {
        if (map != null) {
            final Object answer = map.get(key);
            if (answer instanceof String[]) {
                return (String[]) answer;
            } else if (answer instanceof Object[]) {
                Object[] arr = (Object[]) answer;
                return Stream.of(arr).map(String::valueOf).toArray(String[]::new);
            } else if (answer instanceof Collection) {
                Collection<?> collection = Collection.class.cast(answer);
                return collection.stream().map(String::valueOf).map(String::trim).toArray(String[]::new);
            } else if (answer instanceof String) {
                return splitter.splitAsStream((String) answer).map(String::trim).filter(s -> s.length() > 0).toArray(String[]::new);
            }
        }
        return null;
    }

    public static List<String> getStringList(Map<?, ?> map, String key, Pattern splitter) {
        Object obj = getObject(map, key);
        if (obj instanceof Collection) {
            Collection<?> collection = Collection.class.cast(obj);
            return MoreFunctions.toStringList(collection);
        } else if (obj instanceof Object[]) {
            return MoreFunctions.toStringList((Object[]) obj);
        } else if (obj instanceof String) {
            return MoreFunctions.toStringList((String) obj, splitter);
        }
        return null;
    }

    public static Set<String> getStringSet(Map<?, ?> map, String key, Pattern splitter) {
        Object obj = getObject(map, key);
        if (obj instanceof Collection) {
            Collection<?> collection = Collection.class.cast(obj);
            return MoreFunctions.toStringSet(collection);
        } else if (obj instanceof Object[]) {
            return MoreFunctions.toStringSet((Object[]) obj);
        } else if (obj instanceof String) {
            return MoreFunctions.toStringSet((String) obj, splitter);
        }
        return null;
    }

    // Integer

    public static Integer[] getIntegerArray(Map<?, ?> map, String key, Pattern splitter) {
        Object obj = getObject(map, key);
        if (obj instanceof Collection) {
            Collection<?> collection = Collection.class.cast(obj);
            return MoreFunctions.toIntegerArray(collection);
        } else if (obj instanceof Object[]) {
            return MoreFunctions.toIntegerArray((Object[]) obj);
        } else if (obj instanceof String) {
            return MoreFunctions.toIntegerArray((String) obj, splitter);
        }
        return null;
    }

    public static List<Integer> getIntegerList(Map<?, ?> map, String key, Pattern splitter) {
        Object obj = getObject(map, key);
        if (obj instanceof Collection) {
            Collection<?> collection = Collection.class.cast(obj);
            return MoreFunctions.toIntegerList(collection);
        } else if (obj instanceof Object[]) {
            return MoreFunctions.toIntegerList((Object[]) obj);
        } else if (obj instanceof String) {
            return MoreFunctions.toIntegerList((String) obj, splitter);
        }
        return null;
    }

    public static Set<Integer> getIntegerSet(Map<?, ?> map, String key, Pattern splitter) {
        Object obj = getObject(map, key);
        if (obj instanceof Collection) {
            Collection<?> collection = Collection.class.cast(obj);
            return MoreFunctions.toIntegerSet(collection);
        } else if (obj instanceof Object[]) {
            return MoreFunctions.toIntegerSet((Object[]) obj);
        } else if (obj instanceof String) {
            return MoreFunctions.toIntegerSet((String) obj, splitter);
        }
        return null;
    }

    // Long

    public static Long[] getLongArray(Map<?, ?> map, String key, Pattern splitter) {
        Object obj = getObject(map, key);
        if (obj instanceof Collection) {
            Collection<?> collection = Collection.class.cast(obj);
            return MoreFunctions.toLongArray(collection);
        } else if (obj instanceof Object[]) {
            return MoreFunctions.toLongArray((Object[]) obj);
        } else if (obj instanceof String) {
            return MoreFunctions.toLongArray((String) obj, splitter);
        }
        return null;
    }

    public static List<Long> getLongList(Map<?, ?> map, String key, Pattern splitter) {
        Object obj = getObject(map, key);
        if (obj instanceof Collection) {
            Collection<?> collection = Collection.class.cast(obj);
            return MoreFunctions.toLongList(collection);
        } else if (obj instanceof Object[]) {
            return MoreFunctions.toLongList((Object[]) obj);
        } else if (obj instanceof String) {
            return MoreFunctions.toLongList((String) obj, splitter);
        }
        return null;
    }

    public static Set<Long> getLongSet(Map<?, ?> map, String key, Pattern splitter) {
        Object obj = getObject(map, key);
        if (obj instanceof Collection) {
            Collection<?> collection = Collection.class.cast(obj);
            return MoreFunctions.toLongSet(collection);
        } else if (obj instanceof Object[]) {
            return MoreFunctions.toLongSet((Object[]) obj);
        } else if (obj instanceof String) {
            return MoreFunctions.toLongSet((String) obj, splitter);
        }
        return null;
    }

    // Double

    public static Double[] getDoubleArray(Map<?, ?> map, String key, Pattern splitter) {
        Object obj = getObject(map, key);
        if (obj instanceof Collection) {
            Collection<?> collection = Collection.class.cast(obj);
            return MoreFunctions.toDoubleArray(collection);
        } else if (obj instanceof Object[]) {
            return MoreFunctions.toDoubleArray((Object[]) obj);
        } else if (obj instanceof String) {
            return MoreFunctions.toDoubleArray((String) obj, splitter);
        }
        return null;
    }

    public static List<Double> getDoubleList(Map<?, ?> map, String key, Pattern splitter) {
        Object obj = getObject(map, key);
        if (obj instanceof Collection) {
            Collection<?> collection = Collection.class.cast(obj);
            return MoreFunctions.toDoubleList(collection);
        } else if (obj instanceof Object[]) {
            return MoreFunctions.toDoubleList((Object[]) obj);
        } else if (obj instanceof String) {
            return MoreFunctions.toDoubleList((String) obj, splitter);
        }
        return null;
    }

    public static Set<Double> getDoubleSet(Map<?, ?> map, String key, Pattern splitter) {
        Object obj = getObject(map, key);
        if (obj instanceof Collection) {
            Collection<?> collection = Collection.class.cast(obj);
            return MoreFunctions.toDoubleSet(collection);
        } else if (obj instanceof Object[]) {
            return MoreFunctions.toDoubleSet((Object[]) obj);
        } else if (obj instanceof String) {
            return MoreFunctions.toDoubleSet((String) obj, splitter);
        }
        return null;
    }

}
